#include "lexical.h"

using namespace std;
const char *key_word[8] = {"int", "void", "return", "const", "main", "float", "if", "else"};

bool is_letter(char c)
{
    if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z')
    {
        return true;
    }
    return false;
}

bool is_number(char c)
{
    if (c >= '0' && c <= '9')
    {
        return true;
    }
    return false;
}

const char* my_int2string(int i)
{
    switch (i)
    {
    case 1:
        return "1\0";
    case 2:
        return "2\0";
    case 3:
        return "3\0";
    case 4:
        return "4\0";
    case 5:
        return "5\0";
    case 27:
        return "27\0";
    case 28:
        return "28\0"; 
    case 29:
        return "29\0"; 
    default:
        return NULL;
    }
}

int is_keyword(char *s)
{
    for (int i = 0; i < 5; i++)
    {
        if (!strcmp(key_word[i], s))
        {
            return i+1;
        }
    }
    for (int i = 5; i < 8; i++)
    {
        if (!strcmp(key_word[i], s))
        {
            return i+22;
        }
    }
    return 0;
}

int preprocess(char *in_file_path, char *out_file_path)
{
    FILE *in_file = fopen(in_file_path, "rb");
    FILE *out_file = fopen(out_file_path, "wb");

    if (!in_file)
    {
        perror("Open file error!");
        return EXIT_FAILURE;
    }
    if (!out_file)
    {
        perror("Create pre_processed.txt error!");
        return EXIT_FAILURE;
    }
    char enter_buffer[ENTER_BUFFER_SIZE];
    int read_size = 0;
    int index = 0;
    int size = 0;
    int state = 0;
    char cur_char = ' ';

    int start_ptr = 0;
    while (!feof(in_file))
    {
        index = 0;
        size = 0;
        while (!feof(in_file))
        {
            cur_char = fgetc(in_file);
#if PRE_PROCESS_DEBUG
            cout << state <<" | ";
            if (cur_char == 9 || cur_char == 10 || cur_char == 13)
            {
                cout << cur_char+0 <<" | ";
            }
            else
            {
                cout << cur_char <<" | ";
            }
#endif
            if (cur_char == 9 || cur_char == 10 || cur_char == 13)
            {
#if PRE_PROCESS_DEBUG
                cout << state <<"\n";
#endif
                continue;
            }

            switch (state)
            {
            case 0:
            {
                if (cur_char == ' ')
                    state = 0;
                else if (cur_char == '/')
                    state = 1;
                else if (cur_char == '"')
                    state = 6;
                else
                    state = 2;
                break;
            }
            case 1:
            {
                if (cur_char == '/')
                    state = 3;
                else if (cur_char == '*')
                    state = 4;
                else if (cur_char == ' ')
                    state = 5;
                else
                    state = 2;
                break;
            }
            case 2:
            {
                if (cur_char == '/')
                    state = 1;
                else if (cur_char == ' ')
                    state = 5;
                else if (cur_char == '"')
                    state = 6;
                else
                    state = 2;
                break;
            }
            case 5:
            {
                if (cur_char == ' ')
                    state = 0;
                else if (cur_char == '/')
                    state = 1;
                else if (cur_char == '"')
                    state = 6;
                else
                    state = 2;
                break;
            }
            case 6:
            {
                if (cur_char == '"')
                    state = 7;
                else
                    state = 6;
                break;
            }
            case 7:
            {
                if (cur_char == ' ')
                    state = 5;
                else
                    state = 2;
                break;
            }

            default:
                break;
            }
#if PRE_PROCESS_DEBUG
            cout << state << endl;
#endif
            if (state == 1 || state == 2 || state == 5 || state == 6 || state == 7)
            {
                enter_buffer[index++] = cur_char;
                size++;
            }
            else if (state == 3)
            {
                enter_buffer[index++] = 8;
                size++;
                // index--;
                // size--;

                int c;
                while ((c = fgetc(in_file)) != '\n' && c != EOF)
                    ; // 读取直到行尾或文件末尾
                state = 0;
            }
            else if (state == 4) // 遇到/*，一直不读，直到遇到*/
            {
                enter_buffer[index++] = 8;
                size++;
                // index--;
                // size--;

                char tem_char_1, tem_char_2 = ' ';
                while ((tem_char_1 = fgetc(in_file)) != EOF)
                {
                    if (tem_char_2 == '*' && tem_char_1 == '/')
                    {
                        break;
                    }
                    tem_char_2 = tem_char_1;
                }
                state = 0;
            }
            if (size == ENTER_BUFFER_SIZE)
                break;
        }

        if (start_ptr == ENTER_BUFFER_SIZE)
            start_ptr = 0;
        while (enter_buffer[start_ptr] != -1 && start_ptr < size)
        {
            if (enter_buffer[start_ptr] == 8)
            {
                fseek(out_file, -1, SEEK_CUR);
                fputc(' ', out_file);
                fseek(out_file, -1, SEEK_CUR);
                start_ptr++;
            }
            else
            {
                fputc(enter_buffer[start_ptr++], out_file);
            }
        }
    }
    fputc(' ', out_file);
    fclose(in_file);
    fclose(out_file);
    return EXIT_SUCCESS;
}

int lex_analyze(char *in_file_path, char *out_file_path, Symbol_Table *my_symbol_table)
{
    FILE *in_file = fopen(in_file_path, "rb");
    FILE *out_file = fopen(out_file_path, "wb");

    if (!in_file)
    {
        perror("Open file error!");
        return EXIT_FAILURE;
    }
    if (!out_file)
    {
        perror("Create lex_analyzed.txt error!");
        return EXIT_FAILURE;
    }
    char scan_buffer[SCAN_BUFFER_SIZE];
    char cur_char;
    char* cur_string;
    int cur_lex_len = 0;
    int cur_ptr = 0;
    bool write2left = 1;
    int index = 0;
    int read_size = 0;
    int start_ptr = 0;
    int end_ptr = 0;
    int state = 0;
    //int need_add_minus = 0;
    bool time_to_out = 0;
    //bool //last_is_int = 0;
    while (!feof(in_file))
    {
#if LEX_ANALYZE_DEBUG
        cout << "Write to buffer "<< !write2left<<".\n";
#endif
        index = write2left ? 0 : SCAN_BUFFER_SIZE / 2;
        read_size = fread(&scan_buffer[index], sizeof(char), SCAN_BUFFER_SIZE / 2, in_file);
        write2left = !write2left;
        while (cur_ptr%(SCAN_BUFFER_SIZE/2) < read_size)
        {
            cur_char = scan_buffer[cur_ptr];
#if LEX_ANALYZE_DEBUG
            cout << "start_ptr:" << start_ptr << " start_ptr: "<<end_ptr << " | ";
            cout << state << " | " << cur_char << " | ";
#endif
            switch (state)
            {
            case 0:
            {
                if (cur_char == '_' || is_letter(cur_char))
                {
                    start_ptr = cur_ptr;
                    end_ptr = start_ptr;
                    state = 1;
                }
                else if (is_number(cur_char))
                {
                    start_ptr = cur_ptr;
                    end_ptr = start_ptr;
                    state = 3;
                }
                else if (cur_char == '+')
                {

                    state = 5;
                }
                else if (cur_char == '-')
                {

                    state = 6;
                }
                else if (cur_char == '*')
                {

                    state = 7;
                }
                else if (cur_char == '/')
                {

                    state = 8;
                }
                else if (cur_char == '%')
                {

                    state = 9;
                }
                else if (cur_char == '=')
                {

                    state = 11;
                }
                else if (cur_char == '<')
                {

                    state = 14;
                }
                else if (cur_char == '>')
                {

                    state = 17;
                }
                else if (cur_char == '!')
                {

                    state = 19;
                }
                else if (cur_char == '&')
                {

                    state = 21;
                }
                else if (cur_char == '|')
                {

                    state = 23;
                }
                else if (cur_char == '(')
                {

                    state = 25;
                }
                else if (cur_char == ')')
                {

                    state = 26;
                }
                else if (cur_char == '{')
                {

                    state = 27;
                }
                else if (cur_char == '}')
                {

                    state = 28;
                }
                else if (cur_char == ';')
                {

                    state = 29;
                }
                else if (cur_char == ',')
                {

                    state = 30;
                }
                else
                    state = 0;
                break;
            }
            case 1:
            {
                if (cur_char == '_' || is_letter(cur_char) || is_number(cur_char))
                    state = 1;
                else
                    state = 2;
                break;
            }
            case 3:
            {
                if (is_number(cur_char) || cur_char == '.')
                    state = 3;
                else
                    state = 4;
                break;
            }
            case 11:
            {
                if (cur_char == '=')
                    state = 12;
                else
                    time_to_out = 1;
                break;
            }
            case 14:
            {
                if (cur_char == '=')
                    state = 15;
                else
                    time_to_out = 1;
                break;
            }
            case 17:
            {
                if (cur_char == '=')
                    state = 18;
                else
                    time_to_out = 1;
                break;
            }
            case 19:
            {
                if (cur_char == '=')
                    state = 20;
                else
                    time_to_out = 1;
                break;
            }
            case 21:
            {
                if (cur_char == '&')
                    state = 22;
                else
                    time_to_out = 0; // ERROR
                break;
            }
            case 23:
            {
                if (cur_char == '|')
                    state = 24;
                else
                    time_to_out = 0; // ERROR
                break;
            }
            default:
                break;
            }
#if LEX_ANALYZE_DEBUG
            cout << state << endl;
#endif
            if(state == 1 || state == 3)
            {
                end_ptr = (end_ptr+1)% SCAN_BUFFER_SIZE;
            }
            else if(state == 2) // 识别到关键字/标识符
            {
                cur_lex_len = (start_ptr <= end_ptr) ? end_ptr - start_ptr : SCAN_BUFFER_SIZE - start_ptr + end_ptr;
                cur_string = new char[cur_lex_len+1];
                cur_string[cur_lex_len] = '\0';
                if(start_ptr <= end_ptr)
                {
                    strncpy(cur_string, scan_buffer + start_ptr, cur_lex_len);
                }
                else
                {
                    strncpy(cur_string, scan_buffer + start_ptr, SCAN_BUFFER_SIZE-start_ptr);
                    strncpy(cur_string+SCAN_BUFFER_SIZE-start_ptr, scan_buffer, end_ptr);
                }
                if(is_keyword(cur_string)) //是关键词
                {
                    fputs(cur_string, out_file);
                    fputs("\t", out_file);
                    fputs("<KW,", out_file);
                    fputs(my_int2string(is_keyword(cur_string)), out_file);
                    fputs(">\n", out_file);
                    //last_is_int = 0;

                }
                else
                {
                    fputs(cur_string, out_file);
                    fputs("\t", out_file);
                    fputs("<IDN,", out_file);
                    fputs(cur_string, out_file);
                    fputs(">\n", out_file);
                    my_symbol_table->symbol_table_add((char *)cur_string);
                    //last_is_int = 1;
                }
                delete [] cur_string;
                start_ptr = end_ptr;
                state = 0;
                
                continue;
            }
            else if(state == 4) // 识别到整数或浮点数
            {
                cur_lex_len = (start_ptr <= end_ptr) ? end_ptr - start_ptr : SCAN_BUFFER_SIZE - start_ptr + end_ptr;
                cur_string = new char[cur_lex_len+1];
                cur_string[cur_lex_len] = '\0';
                if(start_ptr <= end_ptr)
                {
                    strncpy(cur_string, scan_buffer + start_ptr, cur_lex_len);
                }
                else
                {
                    strncpy(cur_string, scan_buffer + start_ptr, SCAN_BUFFER_SIZE-start_ptr);
                    strncpy(cur_string+SCAN_BUFFER_SIZE-start_ptr, scan_buffer, end_ptr);
                }
                /*if(need_add_minus){
                    fputc('-', out_file);
                }*/
                fputs(cur_string, out_file);
                fputs("\t", out_file);
                bool is_int = true;
                for(int i = 0; cur_string[i] != '\0';i++){ //识别到浮点数
                    if (cur_string[i] == '.'){
                        fputs("<FLOAT,", out_file);
                        is_int = false;
                        break;
                    }  
                }
                if(is_int){
                    fputs("<INT,", out_file);
                }
                
                /*if(need_add_minus){
                    fputc('-', out_file);
                    need_add_minus = 0;
                }*/
                fputs(cur_string, out_file);
                fputs(">\n", out_file);

                delete [] cur_string;
                start_ptr = end_ptr;
                state = 0;
                //last_is_int = 1;
                continue;
            }
            else if(state == 5) // 识别到 +
            {
                fputs("+\t<OP,6>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 6) // 识别到 -
            {
                //if(last_is_int)
                //{
                fputs("-\t<OP,7>\n", out_file);
                state = 0;
                    //last_is_int = 0;
            }
                /*else{
                    need_add_minus = 1;
                    state = 0;
                }*/
            else if(state == 7) // 识别到 *
            {
                fputs("*\t<OP,8>\n", out_file);
                state = 0;
                // //last_is_int = 0;
            }
            else if(state == 8) // 识别到 /
            {
                fputs("/\t<OP,9>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 9) // 识别到 %
            {
                fputs("%\t<OP,10>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 11 && time_to_out) // 识别到 =
            {
                fputs("=\t<OP,11>\n", out_file);
                time_to_out = 0;
                state = 0;
                //last_is_int = 0;
                continue;
            }
            else if(state == 12) // 识别到 ==
            {
                fputs("==\t<OP,14>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 17 && time_to_out) // 识别到 >
            {
                fputs(">\t<OP,12>\n", out_file);
                time_to_out = 0;
                state = 0;
                //last_is_int = 0;
                continue;
            }
            else if(state == 19 && time_to_out) // 识别到 !
            {
                fputs("!\t<OP,26>\n", out_file);
                time_to_out = 0;
                state = 0;
                //last_is_int = 0;
                continue;
            }
            else if(state == 18) // 识别到 >=
            {
                fputs(">=\t<OP,16>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 14 && time_to_out) // 识别到 <
            {
                fputs("<\t<OP,13>\n", out_file);
                time_to_out = 0;
                state = 0;
                //last_is_int = 0;
                continue;
            }
            else if(state == 15) // 识别到 <=
            {
                fputs("<=\t<OP,15>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 20) // 识别到 !=
            {
                fputs("!=\t<OP,17>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 22) // 识别到 &&
            {
                fputs("&&\t<OP,18>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 24) // 识别到 ||
            {
                fputs("||\t<OP,19>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 25) // 识别到 (
            {
                fputs("(\t<SE,20>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 26) // 识别到 )
            {
                fputs(")\t<SE,21>\n", out_file);
                state = 0;
            }
            else if(state == 27) // 识别到 {
            {
                fputs("{\t<SE,22>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 28) // 识别到 }
            {
                fputs("}\t<SE,23>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 29) // 识别到 ;
            {
                fputs(";\t<SE,24>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            else if(state == 30) // 识别到 ,
            {
                fputs(",\t<SE,25>\n", out_file);
                state = 0;
                //last_is_int = 0;
            }
            cur_ptr++;
            // 若当前字符指针超界，要填充另一半缓冲区
            if(cur_ptr == SCAN_BUFFER_SIZE)
            {
                cur_ptr = 0;
                break;
            }
            if(cur_ptr == SCAN_BUFFER_SIZE/2)
            {
                break;
            }
        }
    }
    fputs("#\t<END,0>\n", out_file);
    fclose(in_file);
    fclose(out_file);
    return EXIT_SUCCESS;
}